package esercitazione3.tda_NodeList;

import java.util.Iterator;

import esercitazione3.exceptions.BoundaryViolationException;
import esercitazione3.exceptions.InvalidPositionException;

public class NodePositionList<E> implements PositionList<E>{

	private int numElements;
	private DNode<E> header, trailer;
	
	public NodePositionList(){
		numElements = 0;
		header = new DNode<E>(null, null, null);
		trailer = new DNode<E>(header, null, null);
		header.setNext(trailer);
	}
	
	DNode<E> checkPosition(Position<E> p) throws InvalidPositionException{
		DNode<E> temp = null;
		
		if(p==null) throw new InvalidPositionException("Posizione nulla passata a NodeList");
		
		if(p==header) throw new InvalidPositionException("Header non e' una posizione valida.");
		
		if(p==trailer) throw new InvalidPositionException("Trailer non e' una posizione valida.");
		
		try{
			temp = (DNode<E>)p;
			
			if(temp.getPrev()==null || temp.getNext()==null)
				throw new InvalidPositionException("Posizione non appartenente ad una valida NodeList");
						
		}catch(ClassCastException e){
			throw new InvalidPositionException("Posizione di tipo sbagliato per questo contenitore.");
		}
		
		return temp;
	}
	
	public int size() {	
		return numElements;
	}

	public boolean isEmpty() {
		return (numElements==0);
	}

	public Position<E> addBefore(Position<E> p, E e)
			throws InvalidPositionException {

		DNode<E> v = checkPosition(p);
		numElements++;
		DNode<E> newNode = new DNode<E>(v.getPrev(), e, v);
		v.getPrev().setNext(newNode);
		v.setPrev(newNode);
		
		return newNode;
	}

	public Position<E> addAfter(Position<E> p, E e)
			throws InvalidPositionException {

		DNode<E> v = checkPosition(p);
		numElements++;
		DNode<E> newNode = new DNode<E>(v, e, v.getNext());
		v.getNext().setPrev(newNode);
		v.setNext(newNode);
		
		return newNode;
	}


	public Position<E> addFirst(E e) {
		numElements++;
		DNode<E> newNode = new DNode<E>(header, e, header.getNext());
		header.getNext().setPrev(newNode);
		header.setNext(newNode);
		
		return newNode;	
	}

	
	public Position<E> addLast(E e) {
		numElements++;
		DNode<E> newNode = new DNode<E>(trailer.getPrev(), e, trailer);
		trailer.getPrev().setNext(newNode);
		trailer.setPrev(newNode);
		
		return newNode;
	}


	public E remove(Position<E> p) throws InvalidPositionException {
		DNode<E> v = checkPosition(p);
		E elem = v.element();
		v.getNext().setPrev(v.getPrev());
		v.getPrev().setNext(v.getNext());
		
		numElements--;
		
		return elem;
	}


	public E set(Position<E> p, E e) throws InvalidPositionException {
		DNode<E> v = checkPosition(p);
		E elem = v.element();
		DNode<E> newNode = new DNode<E>(v.getPrev(), e, v.getNext());
		v.getPrev().setNext(newNode);
		v.getNext().setPrev(newNode);
		
		return elem;
	}

	
	public Position<E> first() throws EmptyListException{
		
		if(header.getNext()==null && trailer.getPrev()==null)
			throw new EmptyListException("La lista e' vuota.");
		
		return header.getNext();
	}


	public Position<E> last() throws EmptyListException{
		
		if(header.getNext()==null && trailer.getPrev()==null)
			throw new EmptyListException("La lista e' vuota.");
		
		return trailer.getPrev();
	}


	public Position<E> prev(Position<E> p) throws InvalidPositionException,
			BoundaryViolationException {
		
		DNode<E> v = checkPosition(p);
		
		if(v.getPrev().equals(header))
			throw new BoundaryViolationException("Si � tentato di accedere alla testa della lista.");
		
		return v.getPrev();
	}


	public Position<E> next(Position<E> p) throws InvalidPositionException,
			BoundaryViolationException {

		DNode<E> v = checkPosition(p);
		
		if(v.getNext().equals(trailer))
			throw new BoundaryViolationException("Si � tentato di accedere alla coda della lista.");
		
		return v.getNext();
	}

	public Iterator<E> iterator() {
		return new ElementIterator<E>(this);
	}

	
	public Iterable<Position<E>> positions() throws EmptyListException, BoundaryViolationException, InvalidPositionException{
		PositionList<Position<E>> toReturn = new NodePositionList<Position<E>>();
		
		if(!isEmpty()){
			Position<E> current = first();
			
			for(int i=0; i<size()-1; i++){
				toReturn.addLast(current);
				current = next(current);
			}
			
			toReturn.addLast(last());
		}
		
		return toReturn;
	}

}
